﻿using System;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Security;
using BGCMS.Models;

namespace BGCMS.Controllers
{
    /// <summary>
    /// ArticleController contains logic connected to individual artices, i.e. creating,
    /// editing, publishing, and connecting/disconnecting keywords to/from them.
    /// Authorization is different for different methods
    /// </summary>
    public class ArticleController : Controller
    {
        #region Variables

        BGRepository rep = null;

        #endregion Variables

        #region Constructor

        public ArticleController()
        {
            rep = new BGRepository();
        }

        #endregion Constructor

        #region Display methods

        /// <summary>
        /// Does nothing but display a NotFound view
        /// </summary>
        /// <returns>Get: /Article/</returns>
        public ActionResult Index()
        {
            return View("NotFound");
        }

        /// <summary>
        /// Displays a particular article and keywords connected to the article
        /// </summary>
        /// <param name="id">The ID of the article</param>
        /// <returns>/Article/Article/[id]</returns>
        public ActionResult Article(int? id)
        {
            if (id.HasValue)
            {
                var article = (from a in rep.GetAllArticles()
                               where a.ID == id.Value
                               select a).SingleOrDefault();

                if (article == null)
                {
                    return View("NotFound");
                }

                // Increment the articles ReadCount counter
                article.ReadCount += 1;
                UpdateModel(article);
                rep.SaveChanges();

                var keywords = from k in rep.GetKeywords()
                               from ka in rep.GetKeywordsInArticle()
                               where k.ID == ka.KeywordID && ka.ArticleID == id.Value
                               select k;

                // The type of the article ("Fræðigrein"/"Reynslusaga"/"Almennt")
                var type = (from m in rep.GetAllArticleTypes()
                            where article.TypeID == m.ID
                            select m.ArticleType1).SingleOrDefault();

                ArticleViewModel model = new ArticleViewModel
                {
                    Keywords = keywords,
                    Article = article,
                    MenuItem = type
                };

                return View(model);
            }
            return View("NotFound");
        }

        #endregion Display methods

        #region Create/Edit methods

        /// <summary>
        /// Displays a form to create a new article
        /// Only accessible to authorized users
        /// </summary>
        /// <returns>Get: /Article/CreateArticle</returns>
        [Authorize]
        public ActionResult CreateArticle()
        {
            return View(new Article());
        }

        /// <summary>
        /// Register a new Article to the database
        /// </summary>
        /// <param name="collection">Form fields filled out by user</param>
        /// <returns>Get: /Article/CreateArticle</returns>
        [Authorize]
        [HttpPost]
        public ActionResult CreateArticle(FormCollection collection)
        {
            Article a = new Article();

            a.DateCreated = DateTime.Now;

            MembershipUser user = Membership.GetUser(User.Identity.Name);
            if (user == null)
            {
                return View("UserNotFound");
            }
            a.AuthorID = (Guid)user.ProviderUserKey;

            try
            {
                UpdateModel(a);

                a.TextBody = System.Web.HttpUtility.HtmlDecode(a.TextBody);
                rep.UpdateArticle(a);
                rep.SaveChanges();
                return View("ArticleSuccess");
            }
            catch
            {   // On failure to save to database the form is redisplayed
                foreach (var issue in a.ArticleGetRuleViolations())
                {
                    ModelState.AddModelError(issue.ArticlePropertyName, issue.ArticleErrorMessage);
                }
                return View(a);
            }
        }

        /// <summary>
        /// Displays a form to edit an existing article.
        /// Only accessible to Administrator users
        /// </summary>
        /// <param name="id">The ID of the article</param>
        /// <returns>Get: /Article/EditArticle/[id]</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult EditArticle(int? id)
        {
            if (id.HasValue)
            {
                var model = (from a in rep.GetAllArticles()
                             where a.ID == id.Value
                             select a).SingleOrDefault();

                if (model == null)
                {
                    return View("NotFound");
                }
                return View(model);
            }
            return View("NotFound");
        }

        /// <summary>
        /// Registers changes made to an existing article to the database
        /// </summary>
        /// <param name="id">The ID of the article</param>
        /// <param name="collection">Form fields filled out by the user</param>
        /// <returns>Get: /Article/PublishedArticles</returns>
        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public ActionResult EditArticle(int id, FormCollection collection)
        {
            var a = (from i in rep.GetAllArticles()
                     where i.ID == id
                     select i).SingleOrDefault();

            if (a == null)
            {
                return View("NotFound");
            }

            try
            {
                UpdateModel(a);
                rep.SaveChanges();

                return RedirectToAction("PublishedArticles");
            }

            catch
            {   // On failure to save to database the form is redisplayed
                foreach (var issue in a.ArticleGetRuleViolations())
                {
                    ModelState.AddModelError(issue.ArticlePropertyName, issue.ArticleErrorMessage);
                }
                return View(a);
            }
        }

        #endregion Create/Edit methods

        #region Article status methods

        /// <summary>
        /// Displays a list of published articles with an option to unpublish them.
        /// Only acccessible to Administrator users.
        /// </summary>
        /// <returns>Get: /Article/PublishedArticles</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult PublishedArticles()
        {
            var model = from a in rep.GetAllArticles()
                        where a.IsPublished == true
                        && a.IsApproved == false    // Variable name reversed, false means the article has not been denied
                        select a;

            return View(model);
        }

        /// <summary>
        /// Displays a list of unpublished articles that have not been denied.
        /// Only accessable to Administrator users.
        /// </summary>
        /// <returns>Get: /Article/UnpublishedArticles</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult UnpublishedArticles()
        {
            var model = from a in rep.GetAllArticles()
                        where a.IsPublished == false
                        && a.IsApproved == false    // Variable name reversed, false means the article has not been denied
                        select a;

            return View(model);
        }

        /// <summary>
        /// Displays a list of articles that have been denied.
        /// Only accessable to Administrator users.
        /// </summary>
        /// <returns>Get: /Article/DeniedArticles</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult DeniedArticles()
        {
            var model = from a in rep.GetAllArticles()
                        where a.IsApproved == true  // Variable name reversed, true means the article been denied
                        select a;

            return View(model);
        }

        /// <summary>
        /// Publishes or denies an article and sends an email to the author indicating the decision
        /// Only accessible to Administrator users.
        /// </summary>
        /// <param name="id">The ID of the article</param>
        /// <param name="approved">Indicates whether the article should be approved (1) or denied (0)</param>
        /// <returns>Get: /Home/Admin</returns>
        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public ActionResult ApproveOrDeny(int? id, int approved)
        {
            var result = (from a in rep.GetAllArticles()
                          where a.ID == id.Value
                          select a).SingleOrDefault();

            if (result == null)
            {
                return View("Error");
            }

            // Get author user information for email purposes
            MembershipUser user = Membership.GetUser(result.AuthorID);
            if (user == null)
            {
                return View("UserNotFound");
            }
            string recipient = user.Email;

            if (id.HasValue)
            {
                if (approved == 1)
                {
                    // Approve the article:
                    result.IsPublished = true;
                    result.IsApproved = false;      // Variable name reversed, false means the article has not been denied

                    StringBuilder article = new StringBuilder("<a href='www.brjostagjof.is/Article/Article/");
                    article.Append(result.ID);

                    // Send email to the author:
                    StringBuilder body = new StringBuilder("<p>Takk fyrir þitt framlag</p>, <p>Greinin ");
                    body.Append(result.Title);
                    body.Append(" Sem þú sendir inn hefur verið samþykkt. Við metum framlag þitt mikils og vonumst til að fá fleiri greinar frá þér í framtíðinni</p> ");
                    body.Append("<p>Þú getur smellt á eftirfarandi hlekk til þess að sjá greinina");
                    body.Append(article);
                    body.Append("' Brjóstagjöf.is</p>");
                    body.Append("<p>Kveðja Brjóstagjöf.is</p>");

                    Email.SendEmail(recipient, "Innsend grein ", body.ToString());
                }
                else if (approved == 0)
                {
                    // Deny the article
                    result.IsPublished = false;
                    result.IsApproved = true;       // Variable name reversed, true means the article been denied

                    // Send email to the author:
                    StringBuilder text = new StringBuilder("<p>Takk fyrir þitt framlag.</p><p> Greinin ");
                    text.Append(result.Title);
                    text.Append(" Sem þú sendir inn hefur því miður verið hafnað, Fyrir frekari upplýsingar getur þú haft samband við okkur á brjostagjof@brjostagjof.is</p> ");
                    text.Append("<p>Kveðja Brjóstagjöf.is</p>");

                    Email.SendEmail(recipient, "Innsend grein ", text.ToString());
                }

                UpdateModel(result);
                rep.SaveChanges();

                return RedirectToAction("Admin", "Home");
            }
            return View("Error");
        }

        #endregion Article status methods

        #region Articles and Keywords

        /// <summary>
        /// Displays a particular article along with its corresponding keywords
        /// and allows the user to add or remove existing keywords to/from the article.
        /// Only accessible to Administrator users.
        /// </summary>
        /// <param name="id">The ID of the Article</param>
        /// <returns>Get: /Article/ReviewArticle/[id]</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult ReviewArticle(int? id)
        {
            if (id.HasValue)
            {
                var article = (from a in rep.GetAllArticles()
                               where a.ID == id.Value
                               select a).SingleOrDefault();

                if (article == null)
                {
                    return View("NotFound");
                }

                // Fetch all keywords connected to the Article:
                var keywords = from k in rep.GetKeywords()
                               from ka in rep.GetKeywordsInArticle()
                               where k.ID == ka.KeywordID
                               && ka.ArticleID == id.Value
                               select k;

                // Fetch all other keywords not connected to the Article:
                var allKeywords = from b in rep.GetKeywords()
                                  where !(from c in keywords
                                          select c.ID).Contains(b.ID)
                                  select b;

                ArticleReviewModel model = new ArticleReviewModel
                {
                    Article = article,
                    Keywords = keywords,
                    AllKeywords = allKeywords
                };

                return View(model);
            }
            return View("NotFound");
        }

        /// <summary>
        /// Removes the connection between a particular Article and a particular Keyword
        /// </summary>
        /// <param name="articleID">The ID of the Article</param>
        /// <param name="keywordID">The ID of the Keyword</param>
        /// <returns>Get: /Article/ReviewArticle/[id]</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult DeleteKeyword(int articleID, int keywordID)
        {
            var result = (from k in rep.GetKeywordsInArticle()
                          where k.ArticleID == articleID
                          && k.KeywordID == keywordID
                          select k).SingleOrDefault();

            if (result == null)
            {
                return View("Error");
            }

            // Delete from database
            rep.DeleteKeywordsInArticles(result);
            rep.SaveChanges();

            return RedirectToAction("ReviewArticle", new { id = articleID });
        }

        /// <summary>
        /// Adds a connection between a particular Article and a particular Keyword
        /// </summary>
        /// <param name="articleID">The ID of the Article</param>
        /// <param name="keywordID">The ID of the Keyword</param>
        /// <returns>Json data indicating success</returns>
        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public ActionResult AddKeyword(int articleID, int keywordID)
        {
            KeywordsInArticle newKeyword = new KeywordsInArticle();

            newKeyword.ArticleID = articleID;
            newKeyword.KeywordID = keywordID;

            rep.AddKeywordsInArticle(newKeyword);
            rep.SaveChanges();

            return Json("Success");
        }

        #endregion Articles and Keywords
    }
}